﻿using Cinch;
using FR.SAFTAnalyser.Model;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Security.Cryptography;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using System.Linq;

namespace FR.SAFTAnalyser.SAFT
{
    internal class SaftAnalyser
    {
        #region singleton properties

        private static volatile SaftAnalyser instance;
        private static object syncRoot = new Object();

        public static SaftAnalyser Instance
        {
            get
            {
                if (instance == null)
                {
                    lock (syncRoot)
                    {
                        if (instance == null)
                            instance = new SaftAnalyser();
                    }
                }

                return instance;
            }
        }

        #endregion

        #region Propriedades públicas

        public string SaftFileName { get; set; }

        public AuditFile SaftFile { get; set; }

        public string PublicKeyFileName { get; set; }

        public string PrivateKeyFileName { get; set; }

        List<Error> mensagensErro;
        public List<Error> MensagensErro
        {
            get
            {
                if (mensagensErro == null)
                    mensagensErro = new List<Error>();
                return mensagensErro;
            }
            set { mensagensErro = value; }
        }

        #endregion

        #region Propriedades privadas
        System.Globalization.CultureInfo en = new System.Globalization.CultureInfo("en-US");
        #endregion

        #region Métodos públicos

        /// <summary>
        /// Loads the SAFT file
        /// </summary>
        public void LoadFileAsync()
        {
            BackgroundWorker bw = new BackgroundWorker();
            bw.DoWork += bw_DoWork;
            bw.RunWorkerCompleted += bw_RunWorkerCompleted;
            bw.RunWorkerAsync(this.SaftFileName);
        }

        /// <summary>
        /// Validate the hashes in the file
        /// </summary>
        public void ValidateHash()
        {
            BackgroundWorker bw = new BackgroundWorker();
            bw.DoWork += bw_DoWorkHash;
            bw.RunWorkerCompleted += bw_RunWorkerCompletedHash;
            bw.RunWorkerAsync(this.SaftFile);
        }

        /// <summary>
        /// Validate the saft file against the schema file
        /// </summary>
        public void ValidateSchema()
        {
            BackgroundWorker bw = new BackgroundWorker();
            bw.DoWork += bw_DoWorkSchema;
            bw.RunWorkerCompleted += bw_RunWorkerCompletedSchema;
            bw.RunWorkerAsync(this.SaftFile);
        }

        /// <summary>
        /// Generate the hash with the provided private key
        /// </summary>
        public void GenerateInvoiceHash()
        {
            BackgroundWorker bw = new BackgroundWorker();
            bw.DoWork += bw_DoWorkGenerateHash;
            bw.RunWorkerCompleted += bw_RunWorkerCompletedGenerateHash;
            bw.RunWorkerAsync(this.SaftFile);
        }

        #endregion Métodos públicos

        #region BackgroundWorker events
        void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled == false && this.SaftFile != null)
                Mediator.Instance.NotifyColleaguesAsync<AuditFile>(MessageType.SAFT_FILE_OPENED, this.SaftFile);

            if (this.MensagensErro != null && e.Cancelled == false)
                Mediator.Instance.NotifyColleaguesAsync<Error[]>(MessageType.ERROR_FOUND, this.MensagensErro.ToArray());
        }

        void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            this.MensagensErro.Clear();

            if (File.Exists(e.Argument.ToString()))
            {
                this.SaftFile = XmlSerializer.Deserialize<AuditFile>(e.Argument.ToString());

                if (this.SaftFile != null)
                {
                    if (this.SaftFile.SourceDocuments != null && this.SaftFile.SourceDocuments.SalesInvoices != null && this.SaftFile.SourceDocuments.SalesInvoices.Invoice != null)
                    {
                        //add the link from the line to the correspondent invoice
                        foreach (var invoice in this.SaftFile.SourceDocuments.SalesInvoices.Invoice)
                        {
                            foreach (var line in invoice.Line)
                            {
                                line.InvoiceNo = invoice.InvoiceNo;
                            }
                        }
                    }

                    //Do validations on fields
                    ValidateHeader(this.SaftFile.Header);
                    if (this.SaftFile.MasterFiles != null)
                    {
                        ValidateCustomers(this.SaftFile.MasterFiles.Customer);
                        ValidateProducts(this.SaftFile.MasterFiles.Product);
                        ValidateSupplier(this.SaftFile.MasterFiles.Supplier);
                        ValidateTax(this.SaftFile.MasterFiles.TaxTable);
                    }
                    if (this.SaftFile.SourceDocuments != null && this.SaftFile.SourceDocuments.SalesInvoices != null)
                        ValidateInvoices(this.SaftFile.SourceDocuments.SalesInvoices);

                    MensagensErro.RemoveAll(c => c == null);
                }
            }
        }

        void bw_RunWorkerCompletedHash(object sender, RunWorkerCompletedEventArgs e)
        {
            if (this.MensagensErro != null && e.Cancelled == false)
                Mediator.Instance.NotifyColleaguesAsync<Error[]>(MessageType.SAFT_HASH_RESULTS, this.MensagensErro.ToArray());
        }

        void bw_DoWorkHash(object sender, DoWorkEventArgs e)
        {
            AuditFile auditFile = e.Argument as AuditFile;

            if (auditFile != null)
                ValidateSaftHash(auditFile);
        }

        void bw_RunWorkerCompletedSchema(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled == false)
                Mediator.Instance.NotifyColleaguesAsync<Error[]>(MessageType.SAFT_SCHEMA_RESULTS, this.MensagensErro.ToArray());
        }

        void bw_DoWorkSchema(object sender, DoWorkEventArgs e)
        {
            ValidaEstruturaXSD();
        }

        void bw_RunWorkerCompletedGenerateHash(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled == false)
                Mediator.Instance.NotifyColleaguesAsync<AuditFile>(MessageType.INVOICE_LIST_CHANGED, this.SaftFile);
        }

        void bw_DoWorkGenerateHash(object sender, DoWorkEventArgs e)
        {
            GenerateHash(this.SaftFile);
        }
        #endregion BackgroundWorker events

        #region metodos privados

        /// <summary>
        /// Generate the hash filed, base in a AuditFile object, the hash will be stored in HashTest field.
        /// </summary>
        void GenerateHash(AuditFile saftfile)
        {
            object hasher = SHA1.Create();

            using (RSACryptoServiceProvider rsaCryptokey = new RSACryptoServiceProvider(1024))
            {
                rsaCryptokey.FromXmlString(GetRSAPrivateKey());

                StringBuilder toHash = new StringBuilder();

                for (int i = 0; i < saftfile.SourceDocuments.SalesInvoices.Invoice.Length; i++)
                {
                    SourceDocumentsSalesInvoicesInvoice invoice = saftfile.SourceDocuments.SalesInvoices.Invoice[i];

                    bool usaHashAnterior = true;
                    if (i == 0 || invoice.InvoiceType != saftfile.SourceDocuments.SalesInvoices.Invoice[i - 1].InvoiceType || Convert.ToInt32(invoice.InvoiceNo.Split('/')[1]) != Convert.ToInt32(saftfile.SourceDocuments.SalesInvoices.Invoice[i - 1].InvoiceNo.Split('/')[1]) + 1)
                        usaHashAnterior = false;

                    FormatStringToHash(ref toHash, invoice, usaHashAnterior ? saftfile.SourceDocuments.SalesInvoices.Invoice[i - 1].Hash : "");

                    byte[] stringToHashBuffer = Encoding.UTF8.GetBytes(toHash.ToString());
                    byte[] r = rsaCryptokey.SignData(stringToHashBuffer, hasher);

                    invoice.HashTest = Convert.ToBase64String(r);
                }
            }
        }

        /// <summary>
        /// Format the correct invoice fields to the spesification of the hash field.
        /// </summary>
        /// <param name="toHash">StringBuilder that will contain the formated string.</param>
        /// <param name="invoice">The invoice.</param>
        /// <param name="hashAnterior">The string hash generated of the last invoice.</param>
        void FormatStringToHash(ref StringBuilder toHash, SourceDocumentsSalesInvoicesInvoice invoice, string hashAnterior)
        {
            toHash.Clear();
            toHash.AppendFormat("{0};{1};{2};{3};{4}"
                        , invoice.InvoiceDate.ToString("yyyy-MM-dd")
                        , invoice.SystemEntryDate.ToString("yyyy-MM-ddTHH:mm:ss")
                        , invoice.InvoiceNo
                        , invoice.DocumentTotals.GrossTotal.ToString("0.00", en)
                        , hashAnterior);
        }

        /// <summary>
        /// Reads the content of the saft file.
        /// </summary>
        /// <returns></returns>
        string GetSAFTContent()
        {
            if (!string.IsNullOrEmpty(this.SaftFileName) || !File.Exists(this.SaftFileName))
                File.ReadAllText(this.SaftFileName);

            return null;
        }

        /// <summary>
        /// Reads the public key file and returns the RSA public key
        /// </summary>
        /// <returns></returns>
        string GetRSAPublicKey()
        {
            if (File.Exists(this.PublicKeyFileName))
            {
                string publickey = File.ReadAllText(this.PublicKeyFileName).Trim();

                if (publickey.StartsWith(RSAKeys.PEM_PUB_HEADER) && publickey.EndsWith(RSAKeys.PEM_PUB_FOOTER))
                {
                    //this is a pem file
                    RSAKeys rsa = new RSAKeys();
                    rsa.DecodePEMKey(publickey);

                    return rsa.PublicKey;
                }
                else
                {
                    return publickey;
                }
            }

            return string.Empty;
        }

        /// <summary>
        /// Reads the public key file and returns the RSA private key
        /// </summary>
        /// <returns></returns>
        string GetRSAPrivateKey()
        {
            if (File.Exists(this.PrivateKeyFileName))
            {
                string privatekey = File.ReadAllText(this.PrivateKeyFileName).Trim();

                if (privatekey.StartsWith(RSAKeys.PEM_PRIV_HEADER) && privatekey.EndsWith(RSAKeys.PEM_PRIV_FOOTER))
                {
                    //this is a pem file
                    RSAKeys rsa = new RSAKeys();
                    rsa.DecodePEMKey(privatekey);

                    return rsa.PrivateKey;
                }
                else
                {
                    return privatekey;
                }
            }

            return string.Empty;
        }

        void ValidateSaftHash(AuditFile auditFile)
        {
            if (!File.Exists(this.SaftFileName))
                return;

            object hasher = SHA1.Create();

            using (RSACryptoServiceProvider rsaCryptokey = new RSACryptoServiceProvider(1024))
            {
                StringBuilder toHash = new StringBuilder();

                try
                {
                    rsaCryptokey.FromXmlString(GetRSAPublicKey());
                }
                catch (Exception ex)
                {
                    this.MensagensErro.Add(new Error { Description = string.Format("Não foi possível ler o ficheiro com a chave pública. {0}", ex.Message), TypeofError = typeof(HashResults) });
                    return;
                }

                for (int i = 0; i < auditFile.SourceDocuments.SalesInvoices.Invoice.Length; i++)
                {
                    SourceDocumentsSalesInvoicesInvoice invoice = auditFile.SourceDocuments.SalesInvoices.Invoice[i];

                    bool usaHashAnterior = true;
                    if (i == 0 || invoice.InvoiceType != auditFile.SourceDocuments.SalesInvoices.Invoice[i - 1].InvoiceType || Convert.ToInt32(invoice.InvoiceNo.Split('/')[1]) != Convert.ToInt32(auditFile.SourceDocuments.SalesInvoices.Invoice[i - 1].InvoiceNo.Split('/')[1]) + 1)
                        usaHashAnterior = false;

                    FormatStringToHash(ref toHash, invoice, usaHashAnterior ? auditFile.SourceDocuments.SalesInvoices.Invoice[i - 1].Hash : "");

                    byte[] stringToHashBuffer = Encoding.UTF8.GetBytes(toHash.ToString());
                    byte[] hashBuffer = Convert.FromBase64String(invoice.Hash);

                    if (rsaCryptokey.VerifyData(stringToHashBuffer, hasher, hashBuffer) == false)
                        this.MensagensErro.Add(new Error { Value = invoice.InvoiceNo, TypeofError = typeof(SourceDocumentsSalesInvoicesInvoice), Field = "Hash", Description = string.Format("A assinatura do documento {0} é inválida.{1}", invoice.InvoiceNo, Environment.NewLine) });
                }
            }
        }

        void ValidaEstruturaXSD()
        {
            try
            {
                XmlSchemaSet schemas = new XmlSchemaSet();
                schemas.Add(null, XmlReader.Create(new StringReader(Properties.Resources.SAFTPT1_02_01)));

                XDocument doc = XDocument.Load(this.SaftFileName);
                doc.Validate(schemas, (o, e) =>
                {
                    this.MensagensErro.Add(new Error { Description = e.Message, TypeofError = typeof(SchemaResults) });
                });
            }
            catch (Exception error)
            {
                // XML Validation failed
                this.MensagensErro.Add(new Error { Description = string.Format("Mensagem de erro: {0}", error.Message), TypeofError = typeof(SchemaResults) });
            }
        }

        void ValidateHeader(Header header)
        {
            MensagensErro.Add(header.ValidateTaxRegistrationNumber());
            MensagensErro.Add(header.ValidateAuditFileVersion());
            MensagensErro.Add(header.ValidateBusinessName());
            MensagensErro.Add(header.ValidateEmail());
            MensagensErro.Add(header.ValidateAddressDetail());
            MensagensErro.Add(header.ValidateBuildingNumber());
            MensagensErro.Add(header.ValidateCity());
            MensagensErro.Add(header.ValidateCountry());
            MensagensErro.Add(header.ValidatePostalCode());
            MensagensErro.Add(header.ValidateRegion());
            MensagensErro.Add(header.ValidateStreetName());
            MensagensErro.Add(header.ValidateCompanyID());
            MensagensErro.Add(header.ValidateCompanyName());
            MensagensErro.Add(header.ValidateCurrencyCode());
            MensagensErro.Add(header.ValidateDateCreated());
            MensagensErro.Add(header.ValidateEndDate());
            MensagensErro.Add(header.ValidateFax());
            MensagensErro.Add(header.ValidateFiscalYear());
            MensagensErro.Add(header.ValidateHeaderComment());
            MensagensErro.Add(header.ValidateProductCompanyTaxID());
            MensagensErro.Add(header.ValidateProductID());
            MensagensErro.Add(header.ValidateProductVersion());
            MensagensErro.Add(header.ValidateSoftwareCertificateNumber());
            MensagensErro.Add(header.ValidateStartDate());
            MensagensErro.Add(header.ValidateTaxAccountingBasis());
            MensagensErro.Add(header.ValidateTaxEntity());
            MensagensErro.Add(header.ValidateTelephone());
            MensagensErro.Add(header.ValidateWebsite());
        }

        void ValidateCustomers(Customer[] customers)
        {
            if (customers != null && customers.Length > 0)
            {
                var duplicated = from p in customers
                                 group p by p.CustomerID into ci
                                 where ci.Count() > 1
                                 select new { codigo = ci.Key, quantidade = ci.Count() };

                foreach (var d in duplicated)
                {
                    string pk = (from p in customers where p.CustomerID.IndexOf(d.codigo, StringComparison.OrdinalIgnoreCase) >= 0 select p.Pk).FirstOrDefault();
                    MensagensErro.Add(new Error { Value = d.codigo, Field = "CustomerID", Description = string.Format("O código {0} está repetido {1} vezes.", d.codigo, d.quantidade), TypeofError = typeof(Product), UID = pk });
                }

                foreach (Customer customer in customers)
                {
                    MensagensErro.Add(customer.ValidateAccountID());
                    MensagensErro.Add(customer.ValidateCompanyName());
                    MensagensErro.Add(customer.ValidateContact());
                    MensagensErro.Add(customer.ValidateCustomerID());
                    MensagensErro.Add(customer.ValidateCustomerTaxID());
                    MensagensErro.Add(customer.ValidateEmail());
                    MensagensErro.Add(customer.ValidateFax());
                    MensagensErro.Add(customer.ValidateSelfBillingIndicator());
                    MensagensErro.Add(customer.ValidateTelephone());
                    MensagensErro.Add(customer.ValidateWebsite());
                }
            }
        }

        void ValidateProducts(Product[] products)
        {
            if (products != null && products.Length > 0)
            {
                var duplicated = from p in products
                                 group p by p.ProductCode into pc
                                 where pc.Count() > 1
                                 select new { codigo = pc.Key, quantidade = pc.Count() };

                foreach (var d in duplicated)
                {
                    string pk = (from p in products where p.ProductCode.IndexOf(d.codigo, StringComparison.OrdinalIgnoreCase) >= 0 select p.Pk).FirstOrDefault();

                    MensagensErro.Add(new Error { Value = d.codigo, Field = "ProductCode", Description = string.Format("O código {0} está repetido {1} vezes.", d.codigo, d.quantidade), TypeofError = typeof(Product), UID = pk });
                }

                foreach (Product product in products)
                {
                    MensagensErro.Add(product.ValidateProductCode());
                    MensagensErro.Add(product.ValidateProductDescription());
                    MensagensErro.Add(product.ValidateProductGroup());
                    MensagensErro.Add(product.ValidateProductNumberCode());
                }
            }
        }

        void ValidateTax(TaxTableEntry[] taxs)
        {
            if (taxs != null && taxs.Length > 0)
            {
                foreach (TaxTableEntry tax in taxs)
                {
                    MensagensErro.Add(tax.ValidateTaxCode());
                    MensagensErro.Add(tax.ValidateTaxCountryRegion());
                }
            }
        }

        void ValidateSupplier(Supplier[] suppliers)
        {
            if (suppliers != null && suppliers.Length > 0)
            {
                foreach (Supplier supplier in suppliers)
                {

                }
            }
        }

        void ValidateGeneralLedger()
        {
        }

        void ValidateGeneralLedgerEntriesJournal()
        {
        }

        void ValidateGeneralLedgerEntriesJournalTransaction()
        {
        }

        void ValidateGeneralLedgerEntriesJournalTransactionLine()
        {
        }

        void ValidateInvoices(SourceDocumentsSalesInvoices invoices)
        {
            if (Convert.ToInt32(invoices.NumberOfEntries) != invoices.Invoice.Length)
                this.MensagensErro.Add(new Error { Value = invoices.NumberOfEntries, Field = "SalesInvoices", TypeofError = typeof(SourceDocumentsSalesInvoices), Description = string.Format("Nº de registos de documentos comerciais incorrecto. Documento: {0}, esperado: ", invoices.NumberOfEntries, invoices.Invoice.Length) });

            foreach (var invoice in invoices.Invoice)
            {
                MensagensErro.Add(invoice.ValidateInvoiceNo());
                MensagensErro.Add(invoice.ValidateHash());
                MensagensErro.Add(invoice.ValidateHashControl());
                MensagensErro.Add(invoice.ValidatePeriod());
                MensagensErro.Add(invoice.ValidateInvoiceDate());
                MensagensErro.Add(invoice.ValidateSelfBillingIndicator());
                MensagensErro.Add(invoice.ValidateSystemEntryDate());
                MensagensErro.Add(invoice.ValidateTransactionID());
                MensagensErro.Add(invoice.ValidateCustomerID());

                decimal total = 0, incidencia = 0, iva = 0;
                int numLinha = 1, num = -1;
                foreach (var line in invoice.Line)
                {
                    if (!string.IsNullOrEmpty(line.LineNumber))
                        Int32.TryParse(line.LineNumber, out num);

                    if (numLinha != num)
                        this.mensagensErro.Add(new Error { Value = line.LineNumber, Field = "LineNumber", TypeofError = typeof(SourceDocumentsSalesInvoicesInvoiceLine), Description = string.Format("Número de linha incorrecto, Documento: {0}, esperado: {1}, valor: {2}", invoice.InvoiceNo, numLinha, line.LineNumber), UID = line.Pk, SupUID = invoice.Pk });
                    numLinha++;

                    ValidateInvoiceLines(line, invoice.Pk, invoice.InvoiceNo);

                    if (string.IsNullOrEmpty(line.UnitOfMeasure))
                        this.MensagensErro.Add(new Error { Value = invoice.InvoiceNo, Field = "UnitOfMeasure", TypeofError = typeof(SourceDocumentsSalesInvoicesInvoiceLine), Description = string.Format("Unidade de medida não preenchida. Documento: {0}, linha nº: ", invoice.InvoiceNo, line.LineNumber), UID = invoice.Pk });
                    if (line.Tax.Item == 0 && string.IsNullOrEmpty(line.TaxExemptionReason))
                        this.MensagensErro.Add(new Error { Value = invoice.InvoiceNo, Field = "TaxExemptionReason", TypeofError = typeof(SourceDocumentsSalesInvoicesInvoiceLine), Description = string.Format("Motivo de isenção do imposto não preenchido. Documento: {0}, linha nº: ", invoice.InvoiceNo, line.LineNumber), UID = invoice.Pk });

                    total += Math.Round(line.UnitPrice * line.Quantity * (1 + line.Tax.Item * 0.01m) * Operation(invoice, line), 2, MidpointRounding.AwayFromZero);
                    incidencia += line.Item * Operation(invoice, line);
                    iva += line.UnitPrice * line.Tax.Item * 0.01m * Operation(invoice, line);
                }
                if (total != invoice.DocumentTotals.GrossTotal)
                    this.MensagensErro.Add(new Error { Value = invoice.InvoiceNo, Field = "GrossTotal", TypeofError = typeof(SourceDocumentsSalesInvoicesInvoice), Description = string.Format("Total errado. Documento: {0}, total: {1} esperado: {2}", invoice.InvoiceNo, invoice.DocumentTotals.GrossTotal, total), UID = invoice.Pk });
                if (incidencia != invoice.DocumentTotals.NetTotal)
                    this.MensagensErro.Add(new Error { Value = invoice.InvoiceNo, Field = "NetTotal", TypeofError = typeof(SourceDocumentsSalesInvoicesInvoice), Description = string.Format("Incidencia errada. Documento {0}, incidencia:{1} esperado:{2}", invoice.InvoiceNo, invoice.DocumentTotals.NetTotal, incidencia), UID = invoice.Pk });
                if (invoice.DocumentTotals.TaxPayable != invoice.DocumentTotals.GrossTotal - invoice.DocumentTotals.NetTotal)
                    this.MensagensErro.Add(new Error { Value = invoice.InvoiceNo, Field = "TaxPayable", TypeofError = typeof(SourceDocumentsSalesInvoicesInvoice), Description = string.Format("Iva errado. Documento: {0}, iva: {1} esperado: {2}", invoice.InvoiceNo, invoice.DocumentTotals.TaxPayable, invoice.DocumentTotals.GrossTotal - invoice.DocumentTotals.NetTotal), UID = invoice.Pk });
                if (invoice.DocumentTotals.GrossTotal != invoice.DocumentTotals.NetTotal + invoice.DocumentTotals.TaxPayable)
                    this.MensagensErro.Add(new Error { Value = invoice.InvoiceNo, Field = "DocumentTotals", TypeofError = typeof(SourceDocumentsSalesInvoicesInvoice), Description = string.Format("Total errado. Documento: {0}, NetTotal + TaxPayable = {1} != GrossTotal", invoice.InvoiceNo, invoice.DocumentTotals.NetTotal + invoice.DocumentTotals.TaxPayable, invoice.DocumentTotals.GrossTotal), UID = invoice.Pk });
            }
        }

        void ValidateInvoiceLines(SourceDocumentsSalesInvoicesInvoiceLine line, string supPk, string invoiceNo)
        {
            this.MensagensErro.Add(line.ValidateLineNumber(SupPk: supPk, invoiceNo: invoiceNo));
            this.MensagensErro.Add(line.ValidateProductCode(SupPk: supPk, invoiceNo: invoiceNo));
            this.MensagensErro.Add(line.ValidateProductDescription(SupPk: supPk, invoiceNo: invoiceNo));
            this.MensagensErro.Add(line.ValidateQuantity(SupPk: supPk, invoiceNo: invoiceNo));
            this.MensagensErro.Add(line.ValidateTaxPointDate(SupPk: supPk, invoiceNo: invoiceNo));
            this.MensagensErro.Add(line.ValidateUnitOfMeasure(SupPk: supPk, invoiceNo: invoiceNo));
            this.MensagensErro.Add(line.ValidateUnitPrice(SupPk: supPk, invoiceNo: invoiceNo));

            int numCasasDecimais = Properties.Settings.Default.NumCasasDecimaisValidacoes;
            bool incidendia = Math.Round(line.UnitPrice * line.Quantity * (1 + line.Tax.Item * 0.01m), numCasasDecimais, MidpointRounding.AwayFromZero) != Math.Round(line.Item, numCasasDecimais, MidpointRounding.AwayFromZero);
            if (incidendia == false)
                this.MensagensErro.Add(new Error { Value = line.Item.ToString(), Field = line.ItemElementName.ToString(), TypeofError = typeof(SourceDocumentsSalesInvoicesInvoice), Description = string.Format("Total de linha diferente do valor unitário * quantidade * taxa de iva. Documento: {0}", line.InvoiceNo), UID = line.Pk, SupUID = supPk });
        }

        private int Operation(SourceDocumentsSalesInvoicesInvoice i, SourceDocumentsSalesInvoicesInvoiceLine l)
        {
            if (i.InvoiceType == InvoiceType.FT || i.InvoiceType == InvoiceType.VD || i.InvoiceType == InvoiceType.ND)
                return l.ItemElementName == ItemChoiceType4.CreditAmount ? 1 : -1;
            else if (i.InvoiceType == InvoiceType.NC)
                return l.ItemElementName == ItemChoiceType4.DebitAmount ? 1 : -1;

            return 1;
        }

        #endregion
    }
}